﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ModbusLib.Base;
using ProtocalLib.Base;

namespace ModbusLib
{
    public class ModbusASCII:ModbusSerial
    {
        public ModbusASCII() :
          this("COM1")
        { }
        public ModbusASCII(string portName) : this(portName, 9600, Parity.None, 8, StopBits.One)
        { }
        public ModbusASCII(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
            : base()
        {
            this.PortName = portName;
            this.BaudRate = baudRate;
            this.Parity = parity;
            this.DataBits = dataBits;
            this.StopBits = stopBits;
        }

        private List<byte> CreateAdu(List<byte> bytes)
        {
            byte lrc = this.LRC(bytes.ToArray());
            bytes.Add(lrc);
            string ascii = ":" + string.Join("", bytes.Select(x => x.ToString("X2")));
            List<byte> buff = Encoding.ASCII.GetBytes(ascii).ToList();
            buff.Add(0x0D);
            buff.Add(0x0A);
            return buff;
        }


        protected override byte[] Check(byte[] resp, int len)
        {
            if (resp.Length == 11 || resp.Length == len)
            {
                // 检查是否完整
                if (resp[0] != 0x3A ||
                resp[resp.Length - 2] != 0x0D ||
                resp[resp.Length - 1] != 0x0A)
                    throw new Exception("响应报文数据不完整");

                // RTU: 01 03 00 00 00 0A LRC校验 
                // ASCII： 3A 30 31 30 33 30 30 ...
                // 去头和尾
                List<byte> ascii = resp.ToList().GetRange(1, resp.Length - 3);
                // 获取对应字符串
                string str = Encoding.ASCII.GetString(ascii.ToArray());
                // 将字符串转变成字节数组
                byte[] datas = Convert.FromHexString(str);

                // LRC校验
                List<byte> check = datas.ToList().GetRange(0, datas.Length - 1);

                check.Add(LRC(check.ToArray()));

                if (!check.SequenceEqual(datas))
                {
                    throw new Exception("数据传输异常，校验码不匹配");
                }

                // 报文异常码检查
                if (datas[1] > 0x80)
                {
                    throw new Exception(ModbusBase.Errors[datas[2]]);// 异常码
                }

                // 截取数据字节
                return datas.ToList().GetRange(3, datas.Length - 4).ToArray();
            }
            else
                throw new Exception("通信响应异常");
        }

        public override byte[] Read(byte slave, FunctionType func, ushort start, ushort count,Action<object> callback = null)
        {
            byte[] datas = GetReadBytes(slave, (byte)func, start, count);
            List<byte> reqBytes = CreateAdu(datas.ToList());
            callback?.Invoke(new LogMessage { MsgType = "Send", Data = reqBytes.ToArray() });

            int len = (count * 2 + 4) * 2 + 3;

            if(func == FunctionType.RCoilStatus || func == FunctionType.RInputCoil)
                len = ((int)Math.Ceiling(count * 1.0 / 8) + 4) * 2 + 3;
            byte[] respBytes = this.SendAndReceive(reqBytes.ToArray(), len);

            // record log
            if (callback != null)
            {
                LogMessage logMessage = new LogMessage { MsgType = "Recv", Data = respBytes };
                if (respBytes[1] > 0x80)
                {
                    logMessage.ErrorMsg = ModbusBase.Errors[respBytes[2]];
                }
                callback.Invoke(logMessage);
            }

            return this.Check(respBytes, len);
        }

        public override void ReadAsync(byte slave, FunctionType func, ushort start, ushort count, int handler, Action<byte[], int, Exception> callback)
        {
            byte[] datas = GetReadBytes(slave, (byte)func, start, count);
            List<byte> reqBytes = CreateAdu(datas.ToList());

            int len = (count * 2 + 4) * 2 + 3;

            if (func == FunctionType.RCoilStatus || func == FunctionType.RInputCoil)
                len = ((int)Math.Ceiling(count * 1.0 / 8) + 4) * 2 + 3;
            this.AsyncModels.Insert(0,new AsyncModel()
            {
               Handler = handler,
               ReqBytes = reqBytes.ToArray(),
               RespLen = len,
               Completed = callback
            });
        }

        public override void Write(byte slave, FunctionType func, ushort start, ushort count, byte[] datas,Action<object> callback = null)
        {
            byte[] buff = GetWriteBytes(slave, (byte)func, start, count, datas);
            List<byte> reqBytes = CreateAdu(buff.ToList());
            callback?.Invoke(new LogMessage { MsgType = "Send", Data = reqBytes.ToArray() });

            int len = 7 * 2 + 3;
            byte[] respBytes = this.SendAndReceive(reqBytes.ToArray(), len);

            // record log
            if (callback != null)
            {
                LogMessage logMessage = new LogMessage { MsgType = "Recv", Data = respBytes };
                if (respBytes[1] > 0x80)
                {
                    logMessage.ErrorMsg = ModbusBase.Errors[respBytes[2]];
                }
                callback.Invoke(logMessage);
            }
            this.Check(respBytes, len);
        }

        public override void WriteAsync(byte slave, FunctionType func, ushort start, ushort count, byte[] datas, int handler, Action<byte[], int, Exception> callback)
        {
            byte[] buff = GetWriteBytes(slave, (byte)func, start, count, datas);
            List<byte> reqBytes = CreateAdu(buff.ToList());
            int len = 7 * 2 + 3;

            this.AsyncModels.Insert(0,new AsyncModel()
            {
                Handler = handler,
                ReqBytes = reqBytes.ToArray(),
                RespLen = len,
                Completed = callback
            });
        }
    }
}
